//
// WinPcap.net
//
// Copyright (C) 2005 Matthew Ward
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Matthew Ward (mrward@users.sourceforge.net)

using System;
using System.Net;

namespace WinPcap
{
	/// <summary>
	/// A TCP network data packet.
	/// </summary>
	public sealed class TcpPacket : Packet
	{
		/// <summary>
		/// The byte index position of the packet's destination port.
		/// </summary>
		public const int DestinationPortPosition = 2;

		/// <summary>
		/// The byte index position of the packet's sequence number.
		/// </summary>
		public const int SequenceNumberPosition = 
			DestinationPortPosition + 2;

		/// <summary>
		/// The byte index position of the packet's acknowledgement number. 
		/// </summary>
		public const int AcknowledgementNumberPosition = 
			SequenceNumberPosition + 4;

		/// <summary>
		/// The byte index position of the packet's dataoffset.
		/// </summary>
		public const int DataOffsetPosition = 12;

		/// <summary>
		/// The control type's (SYN, RST) byte index position.
		/// </summary>
		public const int ControlFlagPosition = 13;

		/// <summary>
		/// The packet's destination ip address.
		/// </summary>
		IPEndPoint m_DestinationAddress;

		/// <summary>
		/// The packet's source ip address.
		/// </summary>
		IPEndPoint m_SourceAddress;

		/// <summary>
		/// The IP packet associated with this tcp packet.
		/// </summary>
		IPPacket m_IPPacket;

		/// <summary>
		/// The packet's sequence number.
		/// </summary>
		uint m_SequenceNumber;
		
		/// <summary>
		/// The packet's acknowledgement number.
		/// </summary>
		uint m_AcknowledgementNumber;

		/// <summary>
		/// The packet's control flag
		/// </summary>
		byte m_ControlFlag;

		/// <summary>
		/// The tcp packet's data.
		/// </summary>
		byte[ ] m_DataBytes;
		
		/// <summary>
		/// The source IP address's port.
		/// </summary>
		int m_SourcePort;
		
		/// <summary>
		/// The destination IP address's port.
		/// </summary>
		int m_DestinationPort;			

		/// <summary>
		/// Creates a new instance of this class.
		/// </summary>
		/// <param name="bytes">The bytes of the packet.</param>
		public TcpPacket( byte[ ] bytes )
			: this( null, bytes, 0 )
		{
		}

		/// <summary>
		/// Creates a new instance of this class.
		/// </summary>
		/// <param name="bytes">The bytes of the packet.</param>
		/// <param name="startIndex">The index that the data
		/// packet starts from in the byte array.</param>
		public TcpPacket( byte[ ] bytes, int startIndex )
			: this( null, bytes, startIndex )
		{
		}

		/// <summary>
		/// Creates a new instance of this class.
		/// </summary>
		/// <param name="packet">The IP packet associated with this
		/// packet.</param>
		/// <param name="bytes">The bytes of the packet.</param>
		/// <param name="startIndex">The index that the data
		/// packet starts from in the byte array.</param>
		public TcpPacket( IPPacket packet, byte[ ] bytes, int startIndex )
			: base( bytes, startIndex )
		{
			m_IPPacket = packet;
			Parse( GetBytes( ) );
		}
		
		/// <summary>
		/// Converts a packet into an TcpPacket.
		/// </summary>
		/// <param name="packet">The packet to convert.</param>
		/// <returns>An TcpPacket if the packet can be converted,
		/// otherwise <see langword="null"/>.</returns>
		public static TcpPacket GetTcpPacket( Packet packet )
		{
			IPPacket ConvertedIPPacket = IPPacket.GetIPPacket( packet );
			
			TcpPacket ConvertedTcpPacket = null;
			
			if( ConvertedIPPacket != null )
			{
				if( ConvertedIPPacket.Protocol == Protocol.Tcp )
				{
					ConvertedTcpPacket = ConvertedIPPacket.InnerPacket as TcpPacket;
				}
			}
			
			return ConvertedTcpPacket;

		}

		/// <summary>
		/// Gets the IP address of the source of this packet.
		/// </summary>
		/// <returns>An IP address end point.</returns>
		public IPEndPoint SourceAddress
		{
			get
			{
				return m_SourceAddress;
			}
		}
		
		/// <summary>
		/// Gets the port of the source IP address of this packet.
		/// </summary>
		/// <returns>An IP address port.</returns>
		public int SourcePort
		{
			get
			{
				return m_SourcePort;
			}
		}

		/// <summary>
		/// Gets the IP address of the destination of this packet.
		/// </summary>
		/// <returns>An IP address end point.</returns>
		public IPEndPoint DestinationAddress
		{
			get
			{
				return m_DestinationAddress;
			}
		}
		
		/// <summary>
		/// Gets the port of the destination IP address of this packet.
		/// </summary>
		/// <returns>An IP address port.</returns>
		public int DestinationPort
		{
			get
			{
				return m_DestinationPort;
			}
		}

		/// <summary>
		/// Gets the sequence number for this packet.
		/// </summary>
		[ CLSCompliant( false ) ]
		public uint SequenceNumber
		{
			get
			{
				return m_SequenceNumber;
			}
		}

		/// <summary>
		/// Gets the acknowledgement number for this packet.
		/// </summary>
		[ CLSCompliant( false ) ]
		public uint AcknowledgementNumber
		{
			get
			{
				return m_AcknowledgementNumber;
			}
		}

		/// <summary>
		/// Gets the control flag for this packet.
		/// </summary>
		public byte ControlFlag
		{
			get
			{
				return m_ControlFlag;
			}
		}

		/// <summary>
		/// Gets the bytes contained inside this packet.  This excludes
		/// any bytes in the TCP header.
		/// </summary>
		/// <returns>The data bytes in byte array.</returns>
		public byte[ ] GetDataBytes( )
		{
			return m_DataBytes;
		}

		/// <summary>
		/// Parses the data bytes.
		/// </summary>
		/// <param name="bytes">The packet's data bytes.</param>
		void Parse( byte[ ] bytes )
		{
			// Get source IP address.
			GetSourceIPAddress( bytes );
	
			// Get destination IP address.
			GetDestinationIPAddress( bytes );
			
			// Get seq no.
			m_SequenceNumber = Packet.GetUInt32( bytes, 
				TcpPacket.SequenceNumberPosition );
	
			// Get ack no.
			m_AcknowledgementNumber = Packet.GetUInt32( bytes, 
				TcpPacket.AcknowledgementNumberPosition );
	
			// Get Control flag
			m_ControlFlag = bytes[ TcpPacket.ControlFlagPosition ];
	
			// Get data
			ExtractDataBytes( bytes );			
		}

		/// <summary>
		/// Extracts the packet's data.
		/// </summary>
		/// <param name="bytes">The packet's data bytes.</param>
		void ExtractDataBytes( byte[ ] bytes )
		{
			int DataOffset = Packet.GetHighOrderNibble( bytes[ TcpPacket.DataOffsetPosition ] );
	
			// Data offset is given as the number of 32 bit fields.
			int HeaderLength = DataOffset * 4;
	
			if( HeaderLength < bytes.Length )
			{
				m_DataBytes = Packet.GetBytesFromByteArray( bytes, 
					HeaderLength,
					bytes.Length - HeaderLength );
			}
			else
			{
				m_DataBytes = new Byte[ 0 ];
			}
		}
		
		/// <summary>
		/// Extracts the source IP address from the packet's
		/// data.
		/// </summary>
		/// <param name="bytes">The packet's data bytes.</param>
		void GetSourceIPAddress( byte[ ] bytes )
		{
			m_SourcePort = Packet.GetUInt16( bytes, 0 );
			
			if( m_IPPacket != null )
			{
				m_SourceAddress = new IPEndPoint( m_IPPacket.Source, m_SourcePort );
			}
			else
			{
				long EmptyIPAddress = 0;
				m_SourceAddress = new IPEndPoint( EmptyIPAddress, m_SourcePort );
			}
		}
		
		/// <summary>
		/// Extracts the destination IP address from the packet's
		/// data.
		/// </summary>
		/// <param name="bytes">The packet's data bytes.</param>
		void GetDestinationIPAddress( byte[ ] bytes )
		{
			m_DestinationPort = Packet.GetUInt16( bytes, TcpPacket.DestinationPortPosition );
	
			if( m_IPPacket != null )
			{
				m_DestinationAddress = new IPEndPoint( m_IPPacket.Destination, m_DestinationPort );
			}
			else
			{
				long EmptyIPAddress = 0;
				m_DestinationAddress = new IPEndPoint( EmptyIPAddress, m_DestinationPort );
			}
		}
	}
}
